package com.arpa.wms.scheduled.service.impl;


import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.StrUtil;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.wms.scheduled.cache.ShopCache;
import com.arpa.wms.scheduled.domain.dto.OmsOrderSetDTO;
import com.arpa.wms.scheduled.domain.entity.OmsOrderSet;
import com.arpa.wms.scheduled.domain.enums.CommonConst;
import com.arpa.wms.scheduled.domain.enums.StatusEnum;
import com.arpa.wms.scheduled.domain.vo.OmsOrderSetVO;
import com.arpa.wms.scheduled.mapper.OmsOrderSetMapper;
import com.arpa.wms.scheduled.service.IOmsOrderSetService;
import com.arpa.wms.scheduled.task.KsTaskJob;
import com.arpa.wms.scheduled.task.PddTaskJob;
import com.arpa.wms.scheduled.task.ScheduledTaskJob;
import com.arpa.wms.scheduled.task.TbTaskJob;
import com.arpa.wms.scheduled.util.SourcePlatformUtils;
import com.arpa.wms.scheduled.util.UUIDUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: rfwang
 * @Date 2021/8/10
 * @Description:
 */
@Service
@Log4j2
public class OmsOrderSetServiceImpl extends ServiceImpl<OmsOrderSetMapper, OmsOrderSet> implements IOmsOrderSetService {

    @Value("${orderPull.pdd}")
    private String pddUrl;
    @Value("${orderPull.tb}")
    private String tbUrl;
    @Value("${orderPull.ks}")
    private String ksUrl;
    /**
     * 可重入锁
     */
    private ReentrantLock lock = new ReentrantLock();
    /**
     * 定时任务线程池
     */
    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;
    @Autowired
    private PartyCache partyCache;
    @Autowired
    private ShopCache shopCache;
    /**
     * 存放已经启动的任务map
     */
    private Map<String, ScheduledFuture> scheduledFutureMap = new ConcurrentHashMap<>();
    private static final Integer SIXTY = 60;
    private static final Integer ONE_THOUSAND_FOUR_HUNDREN_AND_FORTY = 1440;

    /**
     * 重启任务
     *
     * @param code
     * @return java.lang.Boolean
     * @author liuxr
     * @date 2021/8/16 8:39
     */
    @Override
    public Boolean restart(String code) {
        log.info(">>>>>> 进入重启任务 {}  >>>>>>", code);
        //先停止
        this.stop(code);
        //再启动
        return this.start(code);
    }

    /**
     * 停止任务
     *
     * @param code
     * @return java.lang.Boolean
     * @author liuxr
     * @date 2021/8/16 8:42
     */
    @Override
    public Boolean stop(String code) {
        log.info(">>>>>> 进入停止任务 {}  >>>>>>", code);
        //当前任务实例是否存在
        boolean taskStartFlag = scheduledFutureMap.containsKey(code);
        log.info(">>>>>> 当前任务实例是否存在 {}", taskStartFlag);
        if (taskStartFlag) {
            //获取任务实例
            ScheduledFuture scheduledFuture = scheduledFutureMap.get(code);
            //关闭实例
            scheduledFuture.cancel(true);
        }
        log.info(">>>>>> 结束停止任务 {}  >>>>>>", code);
        return taskStartFlag;
    }

    /**
     * 启动任务
     *
     * @param code
     * @return java.lang.Boolean
     * @author liuxr
     * @date 2021/8/16 8:43
     */
    @Override
    public Boolean start(String code) {
        log.info(">>>>>> 启动任务 {} 开始 >>>>>>", code);
        //添加锁放一个线程启动，防止多人启动多次
        log.info(">>>>>> 添加任务启动锁完毕");
        lock.lock();
        try {
            //校验是否已经启动
            if (this.isStart(code)) {
                log.info(">>>>>> 当前任务已经启动，无需重复启动！");
                return false;
            }

            //根据key数据库获取任务配置信息
            OmsOrderSet scheduledTask = getOne(new LambdaQueryWrapper<OmsOrderSet>().eq(OmsOrderSet::getCode,code));
            //启动任务
            this.doStartTask(scheduledTask);
        } finally {
            // 释放锁
            lock.unlock();
            log.info(">>>>>> 释放任务启动锁完毕");
        }
        log.info(">>>>>> 启动任务 {} 结束 >>>>>>", code);
        return true;
    }

    /**
     * 执行启动任务
     */
    private void doStartTask(OmsOrderSet scheduledTask) {
        //任务code
        String code = scheduledTask.getCode();
        //定时表达式
        String taskCron = scheduledTask.getCron();
        //获取需要定时调度的接口
        ScheduledTaskJob scheduledTaskJob ;
        switch (scheduledTask.getPlatformCode()){
            case "PDD" :{
                scheduledTaskJob = new PddTaskJob(code,pddUrl);
                break;
            }
            case "TB" :{
                scheduledTaskJob = new TbTaskJob(code,tbUrl);
                break;
            }
            case "KS" :{
                scheduledTaskJob = new KsTaskJob(code,ksUrl);
                break;
            }
            default:{
                return;
            }

        }

        log.info(">>>>>> 任务 [ {},{} ] ,cron={}", scheduledTask.getPlatformName(),scheduledTask.getShopName(), taskCron);
        ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(scheduledTaskJob,new CronTrigger(taskCron));
        //将启动的任务放入 map
        scheduledFutureMap.put(code, scheduledFuture);
    }

    /**
     * 任务是否已经启动
     */
    private Boolean isStart(String code) {
        //校验是否已经启动
        if (scheduledFutureMap.containsKey(code)) {
            return !scheduledFutureMap.get(code).isCancelled();
        }
        return false;
    }

    /**
     * 程序启动时初始化  ==> 启动所有正常状态的任务
     */
    @Override
    public void initAllTask(List<OmsOrderSet> scheduledTaskBeanList) {
        log.info("程序启动 ==> 初始化所有任务开始 ！size={}", scheduledTaskBeanList.size());
        if (IterUtil.isEmpty(scheduledTaskBeanList)) {
            return;
        }
        for (OmsOrderSet scheduledTask : scheduledTaskBeanList) {
            //任务 code
            String code = scheduledTask.getCode();
            //校验是否已经启动
            if (this.isStart(code)) {
                continue;
            }
            //启动任务
            this.doStartTask(scheduledTask);
        }
        log.info("程序启动 ==> 初始化所有任务结束 ！size={}", scheduledTaskBeanList.size());
    }

    /**
     *  根据code更新启用、停用状态
     * @param codes
     * @param active
     * @return
     */
    @Override
    public Map<String,Object> activeStatus(List<String> codes, StatusEnum active) {
        Map<String,Object> result= new HashMap<>();
        int num = updateStatus(codes,active);
        result.put("success",num);
        return result;
    }

    private int updateStatus(List<String> optCodes,StatusEnum active){
        int num = 0;
        boolean flag = false;
        if(IterUtil.isNotEmpty(optCodes)){
            for(String code: optCodes){
                flag = this.update(new UpdateWrapper<OmsOrderSet>().lambda().set(OmsOrderSet::getStatus, active.getValue())
                        .eq(OmsOrderSet::getCode, code));
                if(flag){
                    num++;
                }
            }
        }
        return num;
    }

    /**
     * 查询列表
     *
     * @param orderSetDTO
     * @return
     */
    @Override
    public List<OmsOrderSetVO> queryList(OmsOrderSetDTO orderSetDTO) {
        List<OmsOrderSetVO> customerVOList = baseMapper.queryList(orderSetDTO);
        return customerVOList;
    }

    /**
     * 查询合计,包含总数
     *
     * @param orderSetDTO
     * @return
     */
    @Override
    public OmsOrderSetVO queryListSum(OmsOrderSetDTO orderSetDTO) {
        OmsOrderSetVO customerVO = baseMapper.queryListSum(orderSetDTO);
        return customerVO;
    }

    /**
     * 保存
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor={Exception.class})
    public boolean save(OmsOrderSet entity) {
        //校验同一个货主，同一个平台，同一个店铺，是否只有一个店铺
        validateOrderSet(entity);
        if(StringUtils.isBlank(entity.getCode())){
            entity.setCode(UUIDUtil.getUUID32());

        }
        if (StrUtil.isEmpty(entity.getPlatformName())) {
            entity.setPlatformName(SourcePlatformUtils.translate(entity.getPlatformCode()));
        }
        if (StrUtil.isEmpty(entity.getShopName())) {
            entity.setShopName(shopCache.translate(entity.getShopCode()));
        }
        if(StringUtils.isBlank(entity.getCreatedBy())){
            entity.setCreatedBy(UserUtil.getCode());
            entity.setCreatedName(partyCache.translate(UserUtil.getCode()));
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }
        entity.setCron(generateCron(entity.getInteval()));
        boolean addFlag = super.save(entity);
        //新增数据并且平台类型为拼多多启动任务
        if (addFlag && CommonConst.PDD.equals(entity.getPlatformCode())) {
            //启动任务
            start(entity.getCode());
        }
        return addFlag;
    }

    private String generateCron(String inteval) {
        String hourStr = StrUtil.EMPTY;
        String minuteStr = StrUtil.EMPTY;
        Integer intevalTime = Integer.parseInt(inteval);
        //超过一个小时的，舍掉分钟取整小时调用
        if (intevalTime >= SIXTY) {
            hourStr =  intevalTime/SIXTY + "";
        } else {
            minuteStr = inteval;
        }
        StringBuilder cronExp = new StringBuilder();
        cronExp.append("0 ");
        if (StrUtil.isNotEmpty(minuteStr)) {
            cronExp.append("*/").append(minuteStr);
            cronExp.append(" ");
            cronExp.append("* ");
        } else {
            cronExp.append("0 ");
            cronExp.append("*/").append(hourStr);
            cronExp.append(" ");
        }
        cronExp.append("* ");
        cronExp.append("* ");
        cronExp.append("?");
        return cronExp.toString();
    }
    private void validateOrderSet(OmsOrderSet entity) {
        //必填项校验
        if (entity == null) {
            throw new ServiceException("新增接单设置不能为空");
        }
        if (StrUtil.isEmpty(entity.getPlatformCode())) {
            throw new ServiceException("平台必填，不能为空");
        }
        if (StrUtil.isEmpty(entity.getShopCode())) {
            throw new ServiceException("店铺必填，不能为空");
        }
        if (StrUtil.isEmpty(entity.getInteval())) {
            throw new ServiceException("时间间隔必填，不能为空");
        }
        try {
            Integer inteval = Integer.parseInt(entity.getInteval());
            if (inteval > ONE_THOUSAND_FOUR_HUNDREN_AND_FORTY) {
                throw new ServiceException("时间间隔不能超过24小时");
            }
        } catch(Exception e) {
            throw new ServiceException("所填数值非整数");
        }


        OmsOrderSet omsOrderSet = this.getOne(new LambdaQueryWrapper<OmsOrderSet>()
                .eq(OmsOrderSet::getShipmentCode, entity.getShipmentCode())
                .eq(OmsOrderSet::getPlatformCode, entity.getPlatformCode())
                .eq(OmsOrderSet::getShopCode, entity.getShopCode()));
        //如果是修改，根据货主、平台、店铺查询出的订单设置排除自身后，如果已经存在，则报错
        if (StrUtil.isNotEmpty(entity.getCode()) && omsOrderSet != null) {
            if (entity.getCode().equals(omsOrderSet.getCode())) {
                omsOrderSet = null;
            }
        }
        if (omsOrderSet != null) {
            throw new ServiceException("入驻商" + omsOrderSet.getShipmentName() + ",平台" + omsOrderSet.getPlatformName() + ",店铺"
                    + omsOrderSet.getShopName() + "已新增接单设置，请确认" );
        }
    }

    /**
     * 根据code更新实体
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor={Exception.class})
    public int updateByCode(OmsOrderSet entity) {
        //校验同一个货主，同一个平台，同一个店铺，是否只有一个店铺
        validateOrderSet(entity);
        entity.setCron(generateCron(entity.getInteval()));
        if(StringUtils.isBlank(entity.getModifiedBy())){
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }
        if (StrUtil.isEmpty(entity.getPlatformName())) {
            entity.setPlatformName(SourcePlatformUtils.translate(entity.getPlatformCode()));
        }
        if (StrUtil.isEmpty(entity.getShopName())) {
            entity.setShopName(shopCache.translate(entity.getShopCode()));
        }
        int num = baseMapper.update(entity,new QueryWrapper<OmsOrderSet>().lambda().eq(OmsOrderSet::getCode,entity.getCode()));
        if (num != 0 && CommonConst.PDD.equals(entity.getPlatformCode())) {
            //重启任务
            restart(entity.getCode());
        }
        return num;
    }

}
